use std::vec;

use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};

use crate::entity;

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PageParam {
    #[serde(default = "PageParam::default_page_index")]
    pub page_index: i64,
    #[serde(default = "PageParam::default_page_size")]
    pub page_size: i64,
}

impl PageParam {
    pub fn start(&self) -> i64 {
        (self.page_index - 1) * self.page_size
    }

    fn default_page_index() -> i64 {
        1
    }

    fn default_page_size() -> i64 {
        50
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PageResult<T> {
    #[serde(default = "PageResult::<T>::default_total")]
    pub total: i64,
    #[serde(default = "PageResult::<T>::default_total_pages")]
    pub total_pages: i64,
    #[serde(default = "PageResult::<T>::default_data")]
    pub data: Vec<T>,
}

impl<T> PageResult<T> {
    pub fn new(total: i64, total_pages: i64, data: Vec<T>) -> Self {
        Self {
            total: total,
            total_pages: total_pages,
            data: data,
        }
    }

    pub fn with_data(total: i64, data: Vec<T>) -> Self {
        Self {
            total: total,
            total_pages: 0,
            data: data,
        }
    }

    pub fn total_pages(&mut self, page_size: i64) {
        if self.total > 0 && page_size > 0 {
            self.total_pages = ((self.total as f64) / (page_size as f64)).ceil() as i64
        }
    }

    fn default_total() -> i64 {
        0
    }

    fn default_total_pages() -> i64 {
        0
    }

    fn default_data() -> Vec<T> {
        vec![]
    }
}

impl<T> Default for PageResult<T> {
    fn default() -> Self {
        Self {
            total: 0,
            total_pages: 0,
            data: vec![],
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProcessParam {
    pub name: String,
    pub params: Vec<String>,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UploadFile {
    pub bucket: String,
    pub prefix: Option<String>,
    pub file_key: Option<String>,
    pub source_file: Option<String>,
    pub source_file_size: Option<i64>,
    pub source_file_type: Option<String>,
    pub source_file_attr: Option<String>,
    pub use_source_filename: Option<i32>,
    pub expired_in_sec: Option<i64>,
    pub process_params: Option<Vec<ProcessParam>>,
    pub process_params_str: Option<String>,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UploadBase64 {
    #[serde(flatten)]
    upload_file: UploadFile,
    data: String,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UploadToken {
    pub file_id: i64,
    pub upload_url: String,
    pub access_key_id: String,
    pub policy: String,
    pub signature: String,
    pub key: String,
    pub url: String,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct App {
    pub id: i64,
    pub provider: String,
    pub access_key_id: String,
    pub access_key_secret: String,
    pub endpoint: String,
    pub inner_endpoint: String,
    pub status: i32,
    pub create_time: NaiveDateTime,
    pub update_time: NaiveDateTime,
}

impl From<entity::app::Model> for App {
    fn from(value: entity::app::Model) -> Self {
        Self {
            id: value.id,
            provider: value.provider,
            access_key_id: value.access_key_id,
            access_key_secret: value.access_key_secret,
            endpoint: value.endpoint,
            inner_endpoint: value.inner_endpoint,
            status: value.status,
            create_time: value.create_time,
            update_time: value.update_time,
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Bucket {
    pub id: i64,
    pub app_id: i64,
    pub bucket_type: i32,
    pub name: String,
    pub domain: String,
    pub process_config: Option<String>,
    pub status: i32,
    pub create_time: NaiveDateTime,
    pub update_time: NaiveDateTime,
}

impl From<entity::bucket::Model> for Bucket {
    fn from(value: entity::bucket::Model) -> Self {
        Self {
            id: value.id,
            app_id: value.app_id,
            bucket_type: value.bucket_type,
            name: value.name,
            domain: value.domain,
            process_config: value.process_config,
            status: value.status,
            create_time: value.create_time,
            update_time: value.update_time,
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct File {
    pub id: i64,
    pub bucket_id: i64,
    pub bucket_name: String,
    pub domain: String,
    pub file_key: String,
    pub source_file: String,
    pub source_file_size: i64,
    pub source_file_type: String,
    pub source_file_attr: String,
    pub url: String,
    pub create_time: NaiveDateTime,
    pub update_time: NaiveDateTime,
}

impl From<&entity::file::Model> for File {
    fn from(value: &entity::file::Model) -> Self {
        Self {
            id: value.id,
            bucket_id: value.bucket_id,
            file_key: value.file_key.clone(),
            source_file: value.source_file.clone(),
            source_file_size: value.source_file_size,
            source_file_attr: value.source_file_attr.clone(),
            create_time: value.create_time,
            update_time: value.update_time,
            ..Default::default()
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileSearchParam {
    pub ids: Option<Vec<i64>>,
    pub file_keys: Option<Vec<String>>,
    pub buckets: Option<Vec<String>>,
    pub expired_in_sec: Option<i64>,
    pub process_params: Option<Vec<ProcessParam>>,
}

impl FileSearchParam {
    pub fn is_empty(&self) -> bool {
        let c = self.clone();

        (c.ids.is_none() || (c.ids.is_some_and(|e| e.is_empty())))
            && (c.file_keys.is_none() || (c.file_keys.is_some_and(|e| e.is_empty())))
            && (c.buckets.is_none() || (c.buckets.is_some_and(|e| e.is_empty())))
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileSearchPageParam {
    #[serde(flatten)]
    pub page_param: PageParam,
    #[serde(flatten)]
    pub file_search_param: FileSearchParam,
}
